10. Regular Expression Matching(Facebook Hard)
作者:
题目:Implement regular expression matching with support for '.'
and '*'
.
'.' Matches any single character. '*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("abc", ".*") → true
isMatch("aab", "c*a*b") → true
思路:'.'匹配任意单字符,但不能匹配空字符;'*'可以匹配0或多个前面一个字符,匹配0个就是将前面的字符删掉,匹配多个代表对前面字符的重复。'*'也可以匹配前面的’.’ 即’.*'可以代表一个'.’ 两个'.’ 或者多个'.’ 每一步匹配都会出现多种条件判别情况 我们用递归和动态规划来分别实现。
题解1:Recursion, 定义一个isMatch递归函数进行匹配,两个变量:s 和 p。先对 p 做条件判断,在此基础上,对 s 做条件判断。
条件1:p.length()==0, s为空返回true,s不空则返回false。
条件2:p.length()==1,
(2-1)s为空返回false;
(2-2)s不为空,p.charAt(0) == s.charAt(0)或者p.charAt(0) == '.'
若满足则返回isMatch(s.substring(1), p.substring(1)),否则为false。
条件3: P.length() >=2,
(3-1)s不空,p.charAt(1)!=’*’,且满足 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'时,若满足则返回isMatch(s.substring(1), p.substring(1)),否则为false。
(3-2)s不空,P.length() >=2 p.charAt(1)==’*’,且满足 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'时,如果该字符只出现一次 if (isMatch(s, p.substring(2))) 返回true,否则重复执行s = s.substring(1)
(3-3)p.charAt(1) == ’*’,返回isMatch(s, p.substring(2))。由上述,我们把条件2的最后一种情况和条件3的第一种情况合并。
Time: O((m+n)x2^(m+n/2)), m=s.length,n=p.length, Space: O(m^2 + n^2) 代码如下:
class Solution {
public boolean isMatch(String s, String p) {
if (p.length()==0) {
return s.length()==0;
}
if (p.length() == 1 || p.charAt(1) != '*') {
if (s.isEmpty() || (p.charAt(0) != '.' && p.charAt(0) != s.charAt(0))){
return false;
} else {
return isMatch(s.substring(1), p.substring(1));
}
}
while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')){
if (isMatch(s, p.substring(2))) {
return true;
}
s = s.substring(1);
}
return isMatch(s, p.substring(2));
}
}
题解2.DP
定义一个boolean dp[i][j],用来表示s[0-i] 与p [0-j] 是否匹配。dp[0][0]表示两个空字符串是否匹配,初始化为true。dp[i][j]为true的条件如下:
条件1, s.charAt(i)=p.charAt(j)
条件2, p.charAt(j)=’.’
条件3, p.charAt(j)=’*’
(3-1)if p.charAt(j-1)!=s.charAt(i),* matches zero of the preceding element. (后面有个例子)
(3-2)if p.charA(j-a)s.charAt(i),或者 p.charAt(j-1)’.’ * Matches or more of the preceding element
(3-2-1) . eg:(“aaab”, "aab") Matches or more of the preceding element, * Matches one
(3-2-2). eg:(“aaa”, “a*”) s.charAt(i-1)=p.charAt(j),* Matches multiple
(3-2-3). eg:(“aaab”, “ab*”) s.charAt(i)=p.charAt(j-1) ,* Matches zero
注意⚠:如右图所示,考虑到这种情况(“aab”, “cab”)中,c*应匹配为空字符串,接下来应尝试匹配aab和a*b。这个子问题的解决应从对应空字符串的匹配开始,即dp[0][2]应设为true。 Time: O(mn),m=s.length,n=p.length Space: O(mn)
代码如下:
class Solution {
public boolean isMatch(String s, String p) {
if (s == null || p == null) {
return false;
}
boolean[][]dp=new boolean[s.length()+1][p.length()+1];
dp[0][0]= true;
//提前考虑到这种情况("aab", "c*a*b"):将c*删除,后即dp[0][2]为接下来的初始值,置为true
for(int i=0;i<p.length();i++){
if(p.charAt(i)=='*'&& dp[0][i-1])
dp[0][i+1]=true;
}
for(int i=0;i<s.length();i++){
for(int j=0;j<p.length();j++ ){
if(p.charAt(j)=='.'){
dp[i+1][j+1]=dp[i][j];
}
if(p.charAt(j)==s.charAt(i)){
dp[i+1][j+1]=dp[i][j];
}
if (p.charAt(j) == '*') {
if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {
dp[i+1][j+1] = dp[i+1][j-1];
}
else {
dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);
}
}
}
}
return dp[s.length()][p.length()];
}
}